module vlib.io.B62;

import vlib.Types;


struct B62 {
	alias typeof(*this) This;
	alias typeof(this) pThis ;
	
	private {
		static const Len	= 64 ;
		static ubyte[Len]	_encode_table ;
		static ubyte[256]
		             			_decode_table ;
	}
	
	static void Init(){
		ubyte*	p	= &_encode_table[0] ;
		
		ubyte a	= 'a' ;
		ubyte Z	= 'Z' ;
		ubyte n	= '0' ;
		
		while( a <= 'z' ) {
			*p	= a ;
			p++;
			a++;

			*p	= Z ;
			p++;
			Z--;
			
			if( a % 2 ) {
				if( n >= '0' && n <= '9' ) {
					*p	= n ;
					p++;
					n++;
				} else if( n is '9' + 1 ){
					*p	= '_' ;
					p++ ;
					n	= '_' ;
				} else if( n is '_' ){
					*p	= '-' ;
					p++;
					n++;
				}
			}
		}
		
		for(int i =0 ;i <Len; i++ ){
			ubyte	v	= _encode_table[i] ;
			_decode_table[v]	= i ;
		}
		
	}
	
	static char[] encodeInt(T)(T t, ubyte[] bu) in {
		assert(bu !is null);
	} body {
		static assert( tango.core.Traits.isIntegerType!(T) ) ;
		if( bu is null || bu.length < Len ){
			return null ;
		}
		ubyte*	p	= cast(ubyte*) &bu[0] ;
		ubyte*	_p	= p ;
		ubyte*	rp	= cast(ubyte*) &bu[$-1] ;
		while( p < rp ){
			if( t < Len ) {
				*p	=	_encode_table[t] ;
				p++ ;
				break ;
			}
			ubyte v	= t % Len ;
			*p	= _encode_table[ v ] ;
			p++;
			t	= t / Len ;
		}
		return cast(char[]) _p[ 0 .. p - _p ];
	}
	
	static T decodeInt(T)(char[] bu) {
		static assert( tango.core.Traits.isIntegerType!(T) ) ;
		if( bu is null || bu.length is 0 ) {
			return T.init ;
		}
		ubyte*	p	= cast(ubyte*) &bu[0] ;
		ubyte*	rp	= cast(ubyte*) &bu[$-1] ;
		T	ret ;
		T	pos	= 1 ;
		while( p <= rp ){
			T	v	= _decode_table[ *p ] * pos ;
			p++;
			ret	+=	v ;
			pos	*=	Len ;
		}
		return ret ;
	}

	static char encodeByte(T)(T t) {
		static assert( T.sizeof is 1 ) ;
		return cast(T) _encode_table[ cast(ubyte) t ];
	}
	
	static char decodeByte(T)(T t) {
		static assert( T.sizeof is 1 ) ;
		return cast(T) _decode_table[ cast(ubyte) t ];
	}

	static alias encodeByte!(ubyte)	enbyte ;
	static alias decodeByte!(ubyte)	debyte ;

	static alias encodeInt!(long)		encode ;
	static alias decodeInt!(long)		decode ;
	
}



unittest{
	B62.Init ;
	ubyte[1024] tmp ;
	long	v	= 2961508005 ;
	char[] ret	= B62.encode(v, tmp) ;
	long 	_v	= B62.decode( ret) ;
	assert( v is _v);
}